package demo.jdk8;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.google.common.collect.Lists;

public class LambdaTest {
	 public static final Comparator<Person> BY_FIRST =
			    Comparator.comparing(Person::getFirstName);
			  public static final Comparator<Person> BY_LAST =
			    Comparator.comparing(Person::getLastName);
			  public static final Comparator<Person> BY_AGE =
			    Comparator.comparing(Person::getAge);
	@Test
	public void testLambda() {
//		ConcurrentHashMap<String, String> hashMap = new ConcurrentHashMap<>();
//		hashMap.put(key)
		List<String> names = Lists.newArrayList("bAr","geEe","Foo");
		Collections.sort(names, (o1, o2) -> o1.compareTo(o2));
		System.out.println(names);
		//names.stream().map((String name) -> {return name.toLowerCase();}).collect(Collectors.toList());
		List<String> lowercaseNames = names.stream().map(name -> name.toLowerCase()).collect(Collectors.toList());
		System.out.println(lowercaseNames);
		
		//变量num 并不需要必须被声明为 final
		int num = 1;
		@SuppressWarnings("unused")
		Converter<Integer, String> stringConverter =
		        (from) -> String.valueOf(from + num);
		 //然而，为了代码可以编译， num 必须隐含为final类型,如下代码不会编译通过
	    // num = 3;
		//区别于局部变量，我们在lambda表达式里对实例域和静态变量具有读写权限。这种行为在匿名对象中是众所周知的
	}
	
	@Test
	public void testStream() {
		List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
//		nums.sort(c);
//		nums.forEach(action);
		System.out.println("sum is:" + 
			nums.stream()
			.filter(num -> num != null)
			.distinct()
			.mapToInt(num -> num * 2)
			.peek(System.out::println)
			.skip(2)
			.limit(4)
			.sum());
		nums.stream().filter(num -> num != null) .sorted().forEach(System.out::println);
		
		List<String> stringCollection = Lists.newArrayList("DDD2", "DDD1", "CCC", "BBB3", "BBB2","AAA2", "AAA1");

		stringCollection.stream()
			.map(String::toLowerCase)
			.sorted((a, b) -> b.compareTo(a))
			.forEach(System.out::println);
		
		boolean anyStartsWithA = stringCollection
			.stream()
			.anyMatch((s) -> s.startsWith("a"));
		System.out.println(anyStartsWithA); // true

		boolean allStartsWithA = stringCollection
			.stream()
			.allMatch((s) -> s.startsWith("a"));
		System.out.println(allStartsWithA); // false
		
		boolean noneStartsWithZ = stringCollection
			.stream()
			.noneMatch((s) -> s.startsWith("z"));
		System.out.println(noneStartsWithZ); // true
		
		Optional<String> reduced = stringCollection
				.stream()
				.sorted()
				.reduce((s1, s2) -> s1 + "#" + s2);
		reduced.ifPresent(System.out::println);
				
		long count = stringCollection.parallelStream().sorted().count();
		System.out.println(count);
		
		final  String s = stringCollection
			    .stream()
			    .collect( Collectors.joining("***"));
		System.err.println( s );
		
		final Map< Character, List< String > > map = stringCollection
				.stream()
				.collect( Collectors.groupingBy( x -> x.charAt(0) ) );
			System.out.println( map );
	}
	
	@Test
	public void testFunctionRef() {
		/**
		    objectName::instanceMethod
			ClassName::staticMethod
			ClassName::instanceMethod
		 */
		List<Person> people = Arrays.asList(
			      new Person("Ted", "Neward", 42),
			      new Person("Charlotte", "Neward", 39),
			      new Person("Michael", "Neward", 19),
			      new Person("Matthew", "Neward", 13),
			      new Person("Neal", "Ford", 45),
			      new Person("Candy", "Ford", 39),
			      new Person("Jeff", "Brown", 43),
			      new Person("Betsy", "Brown", 39)
			    );
		//方法引用, 方法参数签名能匹配上就行
		 Collections.sort(people, Person::compareLastAndAge);

		 Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
		 converter = Integer::valueOf;
		 Integer converted = converter.convert("123");
		 System.out.println(converted);    // 123
		 
		 //构造方法引用，Java编译器通过匹配PersonFactory.create标记自动选择合适的构造器
		 PersonFactory<Person> personFactory = Person::new;
		 @SuppressWarnings("unused")
		Person person = personFactory.create("Peter", "Parker");
	}
	
	@Test
	public  void testFunction(){
		Function<String, Integer> toInteger = Integer::valueOf;
		Function<String, String> backToString = toInteger.andThen(String::valueOf);
		backToString.apply("123");     // "123"
	}
	
	@Test
	public void testSupplier(){
		Supplier<Person> personSupplier = Person::new;
		personSupplier.get();   // new Person
		
		Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
		greeter.accept(new Person("Luke", "Skywalker"));
	}
	
	@Test
	public void testComparator(){
		Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
		Person p1 = new Person("John", "Doe");
		Person p2 = new Person("Alice", "Wonderland");
		comparator.compare(p1, p2);             // > 0
		comparator.reversed().compare(p1, p2);  // < 0

	}
	
	@Test
	public void testOptional(){
		Optional<String> optional = Optional.of("bam");
		optional.isPresent();           // true
		optional.get();                 // "bam"
		optional.orElse("fallback");    // "bam"
		optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"
	}
	
	@SuppressWarnings("unused")
	@Test
	public void testPredicate(){
		Predicate<String> predicate = (s) -> s.length() > 0;

		predicate.test("foo");              // true
		predicate.negate().test("foo");     // false

		Predicate<Object> nonNull = Objects::nonNull;
		Predicate<Object> isNull = Objects::isNull;

		Predicate<String> isEmpty = String::isEmpty;
		Predicate<String> isNotEmpty = isEmpty.negate();
	}
	
	@Test
	public void testMap(){
		Map<Integer, String> map = new HashMap<>();
		for (int i = 0; i < 10; i++) {
			map.putIfAbsent(i, "val" + i); 
			} 
		map.forEach((id, val) -> System.out.println(val));
		
		map.computeIfPresent(3, (num, val) -> val + num);
		map.get(3);             // val33
		map.computeIfPresent(9, (num, val) -> null);
		map.containsKey(9);     // false
		map.computeIfAbsent(23, num -> "val" + num);
		map.containsKey(23);    // true
		map.computeIfAbsent(3, num -> "bam");
		map.get(3);             // val33
		
		map.remove(3, "val3");
		map.get(3);             // val33
		map.remove(3, "val33");
		map.get(3);             // null
		
		map.getOrDefault(42, "not found");
		
		map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
		map.get(9);             // val9
		map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
		map.get(9);             // val9concat
	}
	
	@Test
	public void testFiles() throws IOException{
		String filename="";
	final Path path = new File( filename ).toPath();
	try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
	    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
	}
	}
	
	@Test
	public void testBase64() {
		final String text = "Base64 finally in Java 8!";

		final String encoded = Base64.getEncoder().encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
		System.out.println( encoded );

		final String decoded = new String( Base64.getDecoder().decode( encoded ), StandardCharsets.UTF_8 );
		System.out.println( decoded );
	}
	
	@Test
	public void ParallelArrays(){
		long[] arrayOfLong = new long [ 20000 ];		

        Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );
        System.out.println();

        Arrays.parallelSort( arrayOfLong );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );
        System.out.println();
	}
}

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

interface PersonFactory<P>
{
	P create(String firstName, String lastName);
}

class Person {
	String firstName;
	String lastName;
	int age;
	
	 public static int compareLastAndAge(Person lhs, Person rhs) {
		    if (lhs.lastName.equals(rhs.lastName))
		      return lhs.age - rhs.age;
		    else
		      return lhs.lastName.compareTo(rhs.lastName);
	}
	 public static final Comparator<Person> BY_FIRST =
			    Comparator.comparing(Person::getFirstName);
			  public static final Comparator<Person> BY_LAST =
			    Comparator.comparing(Person::getLastName);
			  public static final Comparator<Person> BY_AGE =
			    Comparator.comparing(Person::getAge);
			  
	public Person(){ }
	 
	public Person(String fn, String ln) {
		this.firstName = fn;
		this.lastName = ln;
	}
	
	public Person(String fn, String ln, int a) {
		this.firstName = fn;
		this.lastName = ln;
		this.age = a;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public int getAge() {
		return age;
	}
}